<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>MP3音频格式测试</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        .container {
            background: white;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        h1 {
            color: #333;
            text-align: center;
            margin-bottom: 30px;
        }
        .test-section {
            margin: 20px 0;
            padding: 20px;
            border: 1px solid #ddd;
            border-radius: 5px;
            background: #f9f9f9;
        }
        button {
            background: #007bff;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 5px;
            cursor: pointer;
            margin: 5px;
        }
        button:hover {
            background: #0056b3;
        }
        button:disabled {
            background: #ccc;
            cursor: not-allowed;
        }
        .status {
            margin: 10px 0;
            padding: 10px;
            border-radius: 3px;
        }
        .info {
            background: #d1ecf1;
            color: #0c5460;
        }
        .success {
            background: #d4edda;
            color: #155724;
        }
        .error {
            background: #f8d7da;
            color: #721c24;
        }
        .audio-info {
            background: #fff3cd;
            color: #856404;
            margin: 10px 0;
            padding: 10px;
            border-radius: 3px;
            font-family: monospace;
            font-size: 12px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>MP3音频格式测试页面</h1>
        
        <div class="test-section">
            <h3>录音测试（MP3格式 - 16kHz/单声道/16bit）</h3>
            <button id="startRecord" onclick="startRecording()">开始录音</button>
            <button id="stopRecord" onclick="stopRecording()" disabled>停止录音</button>
            <button id="submit" onclick="submitAudio()" disabled>提交测试</button>
            <div id="status" class="status info">点击开始录音按钮开始测试</div>
            <div id="audioInfo" class="audio-info" style="display:none;"></div>
        </div>
        
        <div class="test-section">
            <h3>测试日志</h3>
            <div id="log" style="max-height: 300px; overflow-y: auto; background: #f8f9fa; padding: 10px; border-radius: 3px; font-family: monospace; font-size: 12px;"></div>
        </div>
    </div>

    <script>
        let mediaRecorder;
        let audioChunks = [];
        let recordedAudioBlob;
        
        function log(message) {
            const logDiv = document.getElementById('log');
            const timestamp = new Date().toLocaleTimeString();
            logDiv.innerHTML += `[${timestamp}] ${message}<br>`;
            logDiv.scrollTop = logDiv.scrollHeight;
            console.log(message);
        }
        
        async function startRecording() {
            try {
                log('请求麦克风权限...');
                const stream = await navigator.mediaDevices.getUserMedia({ 
                    audio: {
                        sampleRate: 44100,
                        channelCount: 2,
                        echoCancellation: true,
                        noiseSuppression: true
                    } 
                });
                
                log('麦克风权限获取成功，开始录音');
                audioChunks = [];
                
                mediaRecorder = new MediaRecorder(stream);
                log(`MediaRecorder创建成功，MIME类型: ${mediaRecorder.mimeType}`);
                
                mediaRecorder.ondataavailable = (event) => {
                    if (event.data.size > 0) {
                        audioChunks.push(event.data);
                        log(`音频数据块大小: ${event.data.size} bytes`);
                    }
                };
                
                mediaRecorder.onstop = async () => {
                    log('录音停止，开始处理音频数据...');
                    
                    // 检查原始音频格式
                    const audioBlob = new Blob(audioChunks, { type: mediaRecorder.mimeType });
                    await checkAudioFormat(audioBlob, '原始录制音频');
                    
                    // 将录制的音频转换为MP3格式（16k采样率、16bit位深、单声道）
                    try {
                        log('开始转换音频格式到MP3...');
                        const mp3Blob = await convertToMp3(audioBlob);
                        recordedAudioBlob = mp3Blob;
                        await checkAudioFormat(mp3Blob, '转换后MP3音频');
                        
                        document.getElementById('submit').disabled = false;
                        document.getElementById('status').textContent = '录音完成，可以提交测试';
                        document.getElementById('status').className = 'status success';
                        log('音频转换成功，可以提交测试');
                    } catch (error) {
                        log(`音频转换失败: ${error.message}`);
                        recordedAudioBlob = audioBlob; // 回退到原始格式
                        document.getElementById('submit').disabled = false;
                        document.getElementById('status').textContent = '录音完成，可以提交测试（转换失败，使用原始格式）';
                        document.getElementById('status').className = 'status error';
                    }
                };
                
                mediaRecorder.start();
                document.getElementById('startRecord').disabled = true;
                document.getElementById('stopRecord').disabled = false;
                document.getElementById('status').textContent = '正在录音中...';
                document.getElementById('status').className = 'status info';
                
            } catch (error) {
                log(`录音启动失败: ${error.message}`);
                document.getElementById('status').textContent = '录音启动失败: ' + error.message;
                document.getElementById('status').className = 'status error';
            }
        }
        
        function stopRecording() {
            if (mediaRecorder && mediaRecorder.state === 'recording') {
                mediaRecorder.stop();
                mediaRecorder.stream.getTracks().forEach(track => track.stop());
                document.getElementById('startRecord').disabled = false;
                document.getElementById('stopRecord').disabled = true;
                log('录音已停止');
            }
        }
        
        async function submitAudio() {
            if (!recordedAudioBlob) {
                log('没有录制的音频数据');
                return;
            }
            
            try {
                log('开始提交音频数据...');
                const formData = new FormData();
                formData.append('groupId', 'test_group_mp3');
                formData.append('featureId', 'test_mp3_' + Date.now());
                formData.append('audioFile', recordedAudioBlob, 'test_audio.mp3');
                
                log(`提交参数: groupId=test_group_mp3, 文件大小=${recordedAudioBlob.size} bytes`);
                
                const response = await fetch('/api/voiceprint/addFeature', {
                    method: 'POST',
                    body: formData
                });
                
                const result = await response.text();
                log(`服务器响应状态: ${response.status}`);
                log(`服务器响应内容: ${result}`);
                
                if (response.ok) {
                    document.getElementById('status').textContent = '音频提交成功！';
                    document.getElementById('status').className = 'status success';
                } else {
                    document.getElementById('status').textContent = '音频提交失败: ' + result;
                    document.getElementById('status').className = 'status error';
                }
                
            } catch (error) {
                log(`提交失败: ${error.message}`);
                document.getElementById('status').textContent = '提交失败: ' + error.message;
                document.getElementById('status').className = 'status error';
            }
        }
        
        // 检查音频格式信息
        async function checkAudioFormat(audioBlob, label) {
            log(`${label} - 大小: ${audioBlob.size} bytes, MIME类型: ${audioBlob.type}`);
            
            try {
                const audioContext = new (window.AudioContext || window.webkitAudioContext)();
                const arrayBuffer = await audioBlob.arrayBuffer();
                const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
                
                const info = {
                    采样率: audioBuffer.sampleRate + ' Hz',
                    声道数: audioBuffer.numberOfChannels,
                    时长: audioBuffer.duration.toFixed(2) + ' 秒',
                    样本数: audioBuffer.length
                };
                
                log(`${label} - 音频信息: ${JSON.stringify(info)}`);
                
                const audioInfoDiv = document.getElementById('audioInfo');
                audioInfoDiv.style.display = 'block';
                audioInfoDiv.innerHTML = `<strong>${label}</strong><br>` + 
                    Object.entries(info).map(([key, value]) => `${key}: ${value}`).join('<br>');
                    
            } catch (error) {
                log(`${label} - 音频格式检查失败: ${error.message}`);
            }
        }
        
        // 音频格式转换函数 - 转换为MP3格式（16k采样率、16bit位深、单声道）
        async function convertToMp3(audioBlob) {
            return new Promise((resolve, reject) => {
                const audioContext = new (window.AudioContext || window.webkitAudioContext)();
                const fileReader = new FileReader();
                
                fileReader.onload = function(e) {
                    audioContext.decodeAudioData(e.target.result)
                        .then(audioBuffer => {
                            // 重采样到16kHz单声道
                            const resampledBuffer = resampleAudioBuffer(audioBuffer, 16000, 1);
                            // 转换为WAV格式（因为浏览器无法直接生成MP3，后端会处理格式转换）
                            const wavBlob = audioBufferToWav(resampledBuffer);
                            // 重命名为.mp3以便后端正确识别
                            const mp3Blob = new Blob([wavBlob], { type: 'audio/mpeg' });
                            resolve(mp3Blob);
                        })
                        .catch(reject);
                };
                
                fileReader.onerror = reject;
                fileReader.readAsArrayBuffer(audioBlob);
            });
        }

        // 重采样音频缓冲区到指定采样率和声道数
        function resampleAudioBuffer(audioBuffer, targetSampleRate, targetChannels) {
            const sourceSampleRate = audioBuffer.sampleRate;
            const sourceChannels = audioBuffer.numberOfChannels;
            
            // 如果已经是目标格式，直接返回
            if (sourceSampleRate === targetSampleRate && sourceChannels === targetChannels) {
                return audioBuffer;
            }
            
            const audioContext = new (window.AudioContext || window.webkitAudioContext)();
            const ratio = sourceSampleRate / targetSampleRate;
            const targetLength = Math.round(audioBuffer.length / ratio);
            
            // 创建新的音频缓冲区
            const resampledBuffer = audioContext.createBuffer(targetChannels, targetLength, targetSampleRate);
            
            // 处理声道转换和重采样
            for (let channel = 0; channel < targetChannels; channel++) {
                const sourceChannelData = audioBuffer.getChannelData(Math.min(channel, sourceChannels - 1));
                const targetChannelData = resampledBuffer.getChannelData(channel);
                
                // 简单的线性插值重采样
                for (let i = 0; i < targetLength; i++) {
                    const sourceIndex = i * ratio;
                    const index = Math.floor(sourceIndex);
                    const fraction = sourceIndex - index;
                    
                    if (index + 1 < sourceChannelData.length) {
                        targetChannelData[i] = sourceChannelData[index] * (1 - fraction) + sourceChannelData[index + 1] * fraction;
                    } else {
                        targetChannelData[i] = sourceChannelData[index] || 0;
                    }
                }
            }
            
            return resampledBuffer;
        }

        // 将AudioBuffer转换为WAV格式（16bit PCM）
        function audioBufferToWav(buffer) {
            const length = buffer.length;
            const numberOfChannels = buffer.numberOfChannels;
            const sampleRate = buffer.sampleRate;
            const arrayBuffer = new ArrayBuffer(44 + length * numberOfChannels * 2);
            const view = new DataView(arrayBuffer);
            
            // WAV文件头
            const writeString = (offset, string) => {
                for (let i = 0; i < string.length; i++) {
                    view.setUint8(offset + i, string.charCodeAt(i));
                }
            };
            
            writeString(0, 'RIFF');
            view.setUint32(4, 36 + length * numberOfChannels * 2, true);
            writeString(8, 'WAVE');
            writeString(12, 'fmt ');
            view.setUint32(16, 16, true);
            view.setUint16(20, 1, true);
            view.setUint16(22, numberOfChannels, true);
            view.setUint32(24, sampleRate, true);
            view.setUint32(28, sampleRate * numberOfChannels * 2, true);
            view.setUint16(32, numberOfChannels * 2, true);
            view.setUint16(34, 16, true);
            writeString(36, 'data');
            view.setUint32(40, length * numberOfChannels * 2, true);
            
            // 写入音频数据（16bit PCM）
            let offset = 44;
            for (let i = 0; i < length; i++) {
                for (let channel = 0; channel < numberOfChannels; channel++) {
                    const sample = Math.max(-1, Math.min(1, buffer.getChannelData(channel)[i]));
                    view.setInt16(offset, sample < 0 ? sample * 0x8000 : sample * 0x7FFF, true);
                    offset += 2;
                }
            }
            
            return new Blob([arrayBuffer], { type: 'audio/wav' });
        }
    </script>
</body>
</html>